package fr.labri.macchiato.framework.technical.encoder;

import java.util.ArrayList;
import java.util.List;

import org.fusesource.restygwt.client.JsonEncoderDecoder;

import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.smartgwt.client.util.JSON;

/**
 * <p>
 * A generic JSON (de)serializer, using RestyGWT's {@code JsonEncoderDecoder}. Any
 * inheriting class must declare a public interface extending
 * {@code JsonEncoderDecoder} and initialize the {@code codec} field using
 * {@code GWT.create()}.
 * </p>
 * 
 * 
 * @author Matthieu Foucault
 * 
 * 
 * 
 * @param <T>
 *            The class to encode/decode </br> This class has to have a default,
 *            no-arg constructor, and getters and setters for each of its
 *            private fields
 */
public abstract class EncoderGeneric<T> implements Encoder<T> {

	/**
	 * This member has to be initialized in inheriting classes using GWT's
	 * deferred binding
	 */
	protected JsonEncoderDecoder<T> codec;

	@Override
	public String encode(T object) {
		return codec.encode(object).toString();
	}

	@Override
	public T decode(String jsonString) {
		return codec.decode(JSONParser.parseStrict(jsonString));
	}

	/**
	 * Decodes a list of objects obtained from a sparql request
	 */
	@Override
	public List<T> decodeSparqlResult(String jsonString) {

		List<T> result = new ArrayList<T>();

		JSONObject jsObject = new JSONObject(JSON.decode(jsonString));
		JSONObject jsResults = jsObject.get("results").isObject();
		JSONArray bindings = jsResults.get("bindings").isArray();

		for (int i = 0; i < bindings.size(); i++) {

			result.add(codec.decode(recodeJSON(bindings.get(i).isObject())));
		}
		return result;
	}
	
	/**
	 * Retrieve the given field in a JSONArray as a string
	 * 
	 * @param bindings The array from which the field must be retrieved
	 * @param index The index of the binding in which is the field
	 * @param key The name of the field
	 * @return the string corresponding to the given field
	 */
	protected String getAsString(JSONArray bindings, int index, String key) {
		JSONString string = bindings.get(index).isObject().get(key).isObject().get("value").isString();
		
		return (string == null) ? null : string.stringValue();
	}

	private JSONObject recodeJSON(JSONObject object) {
		JSONObject result = new JSONObject();

		for (String key : object.keySet()) {
			JSONString value = new JSONString(object.get(key).isObject()
					.get("value").isString().stringValue());
			result.put(key, value);
		}
		return result;
	}

}
